#!/usr/local/bin/php
<?php
require_once( 'MDB2.php' ) ;

$dsn = '' ;

if ( count( $argv ) < 2 ) {
	die( "DSN argument is required for createnodes. Check your Config.php or myConfig.php file if you do not know it.\n" ) ;
} else {
	$dsn = $argv[1] ;
}

$conn = &MDB2::connect( $dsn ) ;
$conn->loadModule('Manager') ;
$conn->loadModule('Reverse') ;

$suffixes = array( 'php', 'ini' ) ;

// loop over all the tables and views to create ini files for each

$tables = $conn->listTables() ;

foreach( $tables as $table ) {
	$contents = array() ;
	$contents[ 'ini' ] = generateIni( $conn, $table ) ;
	$fileName = generateNodeName( $table ) ;
	$contents[ 'php' ] = generatePhp( $fileName ) ;

	if ( !empty( $contents['ini'] )) {
		foreach( $suffixes as $suffix ) {
			$fileHandle = fopen( "$fileName.$suffix", 'w' ) ;
			if( $fileHandle != FALSE ) {
				fwrite( $fileHandle, $contents[ $suffix ] ) ;
				fclose( $fileHandle ) ;	
				print( "$fileName.$suffix created for table $table\n" ) ;
			} else {
				print( "Unable to write file $fileName.$suffix\n" ) ;
			}	
		}
	} else {
		print( "ERROR: No .ini file generated for table $table\n" ) ;
	}
}

exit() ;


function generateNodeName( $str ) {
	// convert underscore-separated table name to camel case with initial cap
	// if table starts with a single character followed by an underscore, then drop that portion
	// drop final 's' to hopefully make a proper singular form
	
	$str = preg_replace( '/^.{1}_/', '', $str ) ;
	$str = singularForm( $str ) ;
	$str = camelize( $str ) ;	

	return $str ;
	
}

// camelize function stolen shamelessly from CakePHP

function camelize($lowerCaseAndUnderscoredWord) {
	return str_replace(" ", "", ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord)));
}


// the following may or may not be a good idea...stupid english grammar

function singularForm( $str ) {
	$_output = $str ;

	$coreSingularRules = array(
		'/(s)tatuses$/i' => '\1\2tatus',
		'/^(.*)(menu)s$/i' => '\1\2',
		'/(quiz)zes$/i' => '\\1',
		'/(matr)ices$/i' => '\1ix',
		'/(vert|ind)ices$/i' => '\1ex',
		'/^(ox)en/i' => '\1',
		'/(alias)(es)*$/i' => '\1',
		'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
		'/(cris|ax|test)es$/i' => '\1is',
		'/(shoe)s$/i' => '\1',
		'/(o)es$/i' => '\1',
		'/ouses$/' => 'ouse',
		'/uses$/' => 'us',
		'/([m|l])ice$/i' => '\1ouse',
		'/(x|ch|ss|sh)es$/i' => '\1',
		'/(m)ovies$/i' => '\1\2ovie',
		'/(s)eries$/i' => '\1\2eries',
		'/([^aeiouy]|qu)ies$/i' => '\1y',
		'/([lr])ves$/i' => '\1f',
		'/(tive)s$/i' => '\1',
		'/(hive)s$/i' => '\1',
		'/(drive)s$/i' => '\1',
		'/(wave)s$/i' => '\1',
		'/([^fo])ves$/i' => '\1fe',
		'/(^analy)ses$/i' => '\1sis',
		'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
		'/([ti])a$/i' => '\1um',
		'/(p)eople$/i' => '\1\2erson',
		'/(m)en$/i' => '\1an',
		'/(c)hildren$/i' => '\1\2hild',
		'/(n)ews$/i' => '\1\2ews',
		'/^(.*us)$/' => '\\1',
		'/s$/i' => ''
	);

	$coreUninflectedSingular = array(
		'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss', 'Amoyese',
		'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers',
		'cod', 'coitus', 'Congoese', 'contretemps', 'corps', 'debris', 'diabetes', 'djinn', 'eland', 'elk',
		'equipment', 'Faroese', 'flounder', 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
		'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'Kiplingese',
		'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', 'mews', 'moose', 'mumps', 'Nankingese', 'news',
		'nexus', 'Niasese', 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', 'proceedings',
		'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', 'series', 'Shavese', 'shears',
		'siemens', 'species', 'swine', 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese',
		'whiting', 'wildebeest', 'Yengeese'
	);

	$coreIrregularSingular = array(
		'atlases' => 'atlas',
		'beefs' => 'beef',
		'brothers' => 'brother',
		'children' => 'child',
		'corpuses' => 'corpus',
		'cows' => 'cow',
		'ganglions' => 'ganglion',
		'genies' => 'genie',
		'genera' => 'genus',
		'graffiti' => 'graffito',
		'hoofs' => 'hoof',
		'loaves' => 'loaf',
		'men' => 'man',
		'monies' => 'money',
		'mongooses' => 'mongoose',
		'moves' => 'move',
		'mythoi' => 'mythos',
		'numina' => 'numen',
		'occiputs' => 'occiput',
		'octopuses' => 'octopus',
		'opuses' => 'opus',
		'oxen' => 'ox',
		'penises' => 'penis',
		'people' => 'person',
		'sexes' => 'sex',
		'soliloquies' => 'soliloquy',
		'testes' => 'testis',
		'trilbys' => 'trilby',
		'turfs' => 'turf'
	);

	$regexUninflected = enclose( join( '|', $coreUninflectedSingular )) ;
	$regexIrregular = enclose( join( '|', array_keys( $coreIrregularSingular ))) ;
	
	if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $str, $regs)) {
		$_output = $regs[1] . substr($str, 0, 1) . substr($irregular[strtolower($regs[2])], 1);
		return $_output;
	}

	if (preg_match('/^(' . $regexUninflected . ')$/i', $str, $regs)) {
		return $_output;
	}

	foreach ($coreSingularRules as $rule => $replacement) {
		if (preg_match($rule, $str)) {
			$_output = preg_replace($rule, $replacement, $str);
			return $_output;
		}
	}
	
	return $_output;

}

/**
 * Enclose a string for preg matching. Stolen from CakePHP
 *
 * @param string $string String to enclose
 * @return string Enclosed string
 */

function enclose($string) {
	return '(?:' . $string . ')';
}


function generateIni( &$conn, $tableName ) {
	$_output = '' ;

	$tableColumns = $conn->tableInfo( $tableName ) ;
	if ( is_array($tableColumns)) {

		$_output .= "#\n" ;
		$_output .= "# .ini file for $tableName\n" ;
		$_output .= "#\n\n" ;


		$_output .= "[db_parameters]\n\n" ;
		$_output .= "table = $tableName\n" ;
		
		$pkField = "" ;
		
		foreach ($tableColumns as $colData) {
			if (strpos($colData["flags"], "primary_key") !== false) {
				$pkField = $colData["name"] ;
				break ;
			}
		}
		
		if (!empty($pkField)) {
			$_output .= "pkfield = $pkField\n" ;
			$_output .= "default_sortvar = $pkField\n" ;
		} else {
			$_output .= "pkfield = UNKNOWN\n" ;
			$_output .= "default_sortvar = " . $tableColumns[0]["name"] . "\n" ;
		}
		
		$_output .= "default_sortdir = ASC\n" ;


		$_output .= "\n\n[pk_foreign_keys]\n\n" ;

		switch ( $conn->dbsyntax ) {
			case 'mysql':
				$fKeys = $conn->queryAll("select *
						from
						information_schema.key_column_usage ke
						where
						ke.constraint_schema = '{$conn->connected_database_name}' and 
						ke.TABLE_NAME = '$tableName'
						",
						NULL, MDB2_FETCHMODE_ASSOC) ;

				foreach( $fKeys as $fKey ) {
					if ( !empty( $fKey[ 'referenced_table_name' ] )) {
						$_output .= $fKey[ 'column_name' ] . ' = ' . $fKey[ 'referenced_table_name' ]. "\n" ;
					}
					
				}
				break ;
			case 'mssql':
				$fKeys = $conn->queryAll( "EXEC sp_fkeys @fktable_name = $tableName", NULL, MDB2_FETCHMODE_ASSOC ) ;
				foreach( $fKeys as $fKey ) {
					$_output .= $fKey[ 'fkcolumn_name' ] . ' = ' . $fKey[ 'pktable_name' ]. "\n" ;
				}
				break ;
			default:
				break ;
		}



		$_output .= "\n\n[fields]\n\n" ;
		foreach( $tableColumns as $index => $colData ) {
			$_output .= "$index = {$colData['name']}\n" ;
		}

		$_output .= "\n\n[field_definitions]\n\n" ;

		foreach ($tableColumns as $colData) {
			$_output .= $colData["name"] . " = " ;
			
			if (strpos($colData["flags"], "primary_key") !== false) {
				$_output .= "DB_DATATYPE_KEY" ;
			} elseif ($colData["type"] == "int" && substr($colData["name"],-3) == "_id") {
				$_output .= "DB_DATATYPE_KEY" ;
			} else {
				switch ($colData["type"]) {
					case 'varchar':
					case 'char':
					case 'text':
					case 'mediumtext':
					case 'enum':
						$_output .= 'DB_DATATYPE_STRING_BASIC';
						break;
					case 'int':
						$_output .= 'DB_DATATYPE_INTEGER';
						break;
					case 'real':
					case 'float':
						$_output .= 'DB_DATATYPE_FLOAT';
						break;
					case 'date':
						$_output .= 'DB_DATATYPE_DATE' ;
						break ;
					case 'datetime':
						$_output .= 'DB_DATATYPE_DATETIME';
						break;
					default:
						$_output .= 'UNSURE ('.$colData["type"].')';
						break;	   	
				}
			}
			$_output .= "\n" ;
		}

		$_output .= "\n\n[field_aliases]\n\n" ;
		
		foreach ($tableColumns as $colData) {
			$humanName = ucwords(strtolower(str_replace("_", " ", $colData["name"]))) ;
			$_output .= "{$colData['name']} = $humanName\n" ;
		}	
		
		$_output .= "\n\n[field_default_values]\n\n" ;

		foreach ($tableColumns as $colData) {
			$_output .= "{$colData['name']} = null\n" ;
		}

		$_output .= "\n\n[field_values_required]\n\n" ;

		foreach ($tableColumns as $colData) {
			$_output .= $colData["name"] . " = " ;
			if (strpos($colData["flags"], "not_null") !== false) {
				$_output .= "true" ;
			} else {
				$_output .= "false" ;				
			}
			$_output .= "\n" ;
		}

	}





	return $_output ;
	
}

function showDebug( $var, $die = true ) {
	print_r( $var ) ;
	print("\n");
	if ( $die ) die() ;
}


function generatePhp( $nodeName ) {

$_output = "<?php

/**
 * Dao_Node_$nodeName
 *
 * @author Application Support Group
 * @copyright University of Georgia
 * @license http://www.opensource.org/licenses/gpl-license.php
 * @filesource
*/

class Dao_Node_$nodeName extends Framework_DB_DaoNode {

	/**
	 * @author Application Support Group
	 * @access public
	 * @return void
	*/
	public function __construct(\$pk_value=null, \$doQuery=true ) {
		\$schemaFile = str_replace( '.php', '.ini', __FILE__ ) ;
		parent::__construct( \$schemaFile );

		// we may already have the data
		if( !empty( \$pk_value ) && \$doQuery ) {
			\$this->retrieve( \$pk_value ) ;
		}
	}


	public static function getSchemaFile() {
		return str_replace( '.php', '.ini', __FILE__ ) ;
	}


	/**
	 * __destruct
	 *
	 * @author Application Support Group
	 * @access public
	 * @return void
	*/
	public function __destruct() {
		parent::__destruct();
	}
}

?>" ;
	return $_output ;


}


?>